package com.mytrip.base.dao.impl;

import java.io.Serializable;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
import org.springframework.util.ClassUtils;

import com.mytrip.base.dao.BaseDao;
import com.mytrip.base.dto.BaseDto;
import com.mytrip.base.exception.BusinessException;
import com.mytrip.base.model.BaseModel;
import com.mytrip.dto.Pagination;
import com.mytrip.util.BeanUtil;
import com.mytrip.util.reqcache.ReqCacheable;

/**
 * Data Access Object (Dao) class. This is an base class used to tag our Dao
 * classes and to provide common methods to all Daos.
 * 
 * @author evan
 * @version 0.1, 2008-3-9
 * @since   JDK1.6
 */
public abstract class BaseDaoImpl <T extends BaseModel, PK extends Serializable> 
		extends SqlMapClientDaoSupport implements BaseDao<T, PK> {
	
    /**
     * Log variable for all child classes. Uses LogFactory.getLog(getClass()) from Commons Logging
     */
    protected final Log log = LogFactory.getLog(getClass());
    private Class<T> persistentClass;

    /**
     * Constructor that takes in a class to see which type of entity to persist
     * @param persistentClass the class type you'd like to persist
     */
    public BaseDaoImpl(final Class<T> persistentClass) {
        this.persistentClass = persistentClass;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public List<T> getAll() {
        return getSqlMapClientTemplate().queryForList(
                IBatisDaoUtils.getAllObjectQuery(ClassUtils.getShortName(this.persistentClass)));
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public T get(PK id) {
        T object = (T) getSqlMapClientTemplate().queryForObject(
                IBatisDaoUtils.getObjectQuery(ClassUtils.getShortName(this.persistentClass)), id);
        if (object == null) {
            log.warn("Uh oh, '" + this.persistentClass + "' object with id '" + id + "' not found...");
            //throw new ObjectRetrievalFailureException(ClassUtils.getShortName(this.persistentClass), id);
        	BusinessException e = new BusinessException("D0000002");
        	e.addParam(ClassUtils.getShortName(this.persistentClass));
        	e.addParam(id.toString());
        	throw e;            
        }
        return object;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public boolean exists(PK id) {
        T object = (T) getSqlMapClientTemplate().queryForObject(
                IBatisDaoUtils.getObjectQuery(ClassUtils.getShortName(this.persistentClass)), id);
        return object != null;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public T save(final T object) {
        String className = ClassUtils.getShortName(object.getClass());
        Object primaryKey = IBatisDaoUtils.getPrimaryKeyValue(object);
        int affectRecNum = 0;
        
        String keyId = null;

        // check for null id
        if (primaryKey != null 
        		&& ((Long) primaryKey).longValue() != 0L) {
            keyId = primaryKey.toString();
        }

        // check for new record
        if (StringUtils.isBlank(keyId)) {
            IBatisDaoUtils.prepareObjectForSaveOrUpdate(object);
            primaryKey = getSqlMapClientTemplate().insert(IBatisDaoUtils.getInsertQuery(className), object);
            IBatisDaoUtils.setPrimaryKey(object, primaryKey);
        } else {
            IBatisDaoUtils.prepareObjectForSaveOrUpdate(object);
            affectRecNum = getSqlMapClientTemplate().update(IBatisDaoUtils.getUpdateQuery(className), object);
            if (affectRecNum == 0){
            	BusinessException e = new BusinessException("D0000001");
            	String parma = (primaryKey == null || ((Long) primaryKey).longValue() != 0L) ? null : primaryKey.toString();
            	e.addParam(parma);
            	throw e;
            }
            if(object instanceof ReqCacheable){
            	((ReqCacheable)object).freshReqCache();
            }
        }

        // check for null id
        if (IBatisDaoUtils.getPrimaryKeyValue(object) == null) {
            //throw new ObjectRetrievalFailureException(className, object);
            log.warn("Uh oh, '" + this.persistentClass + "' object with id '" + primaryKey + "' not found...");

        	BusinessException e = new BusinessException("D0000002");
        	e.addParam(ClassUtils.getShortName(this.persistentClass));
        	String parma = (primaryKey == null || ((Long) primaryKey).longValue() != 0L) ? null : primaryKey.toString();
        	e.addParam(parma);
        	throw e;         	
        } else {
            return object;
        }
    }

    /**
     * {@inheritDoc}
     */
    public void updateSelective(T object){
        String className = ClassUtils.getShortName(object.getClass());
        Object primaryKey = IBatisDaoUtils.getPrimaryKeyValue(object);

        // check for null id
        if (primaryKey != null && ((Long) primaryKey).longValue() != 0L) {
            IBatisDaoUtils.prepareObjectForSaveOrUpdate(object);
            getSqlMapClientTemplate().update(IBatisDaoUtils.getUpdateSelectiveQuery(className), object);
            if(object instanceof ReqCacheable){
            	((ReqCacheable)object).freshReqCache();
            }
        } else {
        	//throw new ObjectRetrievalFailureException(className, object);
            log.warn("Uh oh, '" + this.persistentClass + "' object with id '" + primaryKey + "' not found...");

        	BusinessException e = new BusinessException("D0000002");
        	e.addParam(ClassUtils.getShortName(this.persistentClass));
        	String parma = (primaryKey == null || ((Long) primaryKey).longValue() != 0L) ? null : primaryKey.toString();
        	e.addParam(parma);
        	throw e; 
        }
    }
    
    /**
     * {@inheritDoc}
     */
    public void remove(PK id) {
        getSqlMapClientTemplate().update(
                IBatisDaoUtils.getRemoveQuery(ClassUtils.getShortName(this.persistentClass)), id);
    }
    
    @SuppressWarnings("unchecked")
	public Pagination findObjectsWithPg(String statementName, BaseDto dto){
    	Pagination pg = new Pagination();
    	
    	//dto.setNeedCount(true);
    	Object count= getSqlMapClientTemplate().queryForObject(IBatisDaoUtils.getCountQuery(statementName), dto);
		int totalCount = Integer.parseInt(count.toString());		
		
		dto.setTotalCount(totalCount);
		dto.calStart();
		//dto.setNeedCount(false);		
		List rstList = getSqlMapClientTemplate()
				.queryForList(statementName, dto);

		BeanUtil.copyProperties(pg, dto);
		pg.setTotalCount(totalCount);
		pg.setResultList(rstList);

    	return pg;
    }
    
}
